import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Stack;

public class Parser
{
	public static void main(String args[]) throws Exception
	{
		Scanner scan = new Scanner();
		String toks = "";
		String [] tokens;
		Grammar g = new Grammar();
		Hashtable<Point, Rule> table;
		Stack<String> s = new Stack<String>();
		if(Integer.parseInt(args[0]) == 1)
		{
			if(args.length == 4)
			{
				scan.scanAndPrint(args[1], args[2]);
				toks = scan.readFile(args[2]);
				g.readGrammarFromFile(args[3]);
			}
		}
		else if(args.length == 3)
		{
			toks = scan.readFile(args[1]);
			g.readGrammarFromFile(args[2]);
		}
		else
		{
			System.out.println("Usage:\n"+
					"1 path.tiny path.tokens TINY.grammar\nOR\n" + 
			"0 path.tokens LL1.grammar");
			System.exit(0);
		}

		g.generateParseTable();
		table = g.getParseTable();
		tokens = toks.split(" ");
		s.push("$");
		s.push(g.getStart().getName());
		int i = 0;
		while(!s.peek().equals("$"))
		{
			if(Grammar.find(g.getTerminals(), s.peek().trim())!= -1)
			{
				if(tokens[i].equals(s.peek()))
				{
					i++;
					s.pop();
				}
			}
			else if(Grammar.find(g.getGrammar(), s.peek().trim())!= -1)
			{
				if(tokens[i].equals("$") || Grammar.find(g.getTerminals(),tokens[i]) >= 0)
				{
					Point p;
					if(tokens[i].equals("$"))
						p = new Point(Grammar.find(g.getGrammar(), s.peek()), 
								g.getTerminals().size());
					else
						p = new Point(Grammar.find(g.getGrammar(), s.peek()), 
							Grammar.find(g.getTerminals(), tokens[i]));
					
					int idx = Grammar.find(g.getPoints(), p);
					Rule r = null;
					if(idx >= 0)
						r = table.get(g.getPoints().get(idx));
					if(r != null)
					{
						s.pop();
						String s1 = r.product;
						ArrayList<String> derivs = new ArrayList<String>();

						while(s1.length() > 0)
						{
							if(s1.contains("\\epsilon"))
							{
								s1 = "";
								break;
							}
							for(int k = 0; k < g.getGrammar().size(); k++)
							{
								if(s1.trim().startsWith(g.getGrammar().get(k).getName()))
								{
									derivs.add(g.getGrammar().get(k).getName());
									s1 = s1.substring(s1.indexOf(g.getGrammar().get(k).getName())+
											g.getGrammar().get(k).getName().length());
								}
							}

							for(int k = 0; k < g.getTerminals().size(); k++)
							{
								if(s1.trim().startsWith(g.getTerminals().get(k).getName()))
								{
									derivs.add(g.getTerminals().get(k).getName());
									s1 = s1.substring(s1.indexOf(g.getTerminals().get(k).getName())+
											g.getTerminals().get(k).getName().length());
								}
							}
						}

						for(int m = derivs.size()-1; m >= 0 ; m--)
						{
							s.push(derivs.get(m));
						}
					}
					else
					{
						throw new Exception("Error on token " +  tokens[i] + " for " + 
								s.peek());
					}
				}
			}
			else
			{
				throw new Exception("Error on token " +  tokens[i] + " for " + 
						s.peek());
			}
		}
		if(s.peek().equals("$") && tokens[i].equals("$"))
			System.out.println("Successful Parse!");
		else
		{
			throw new Exception("Error on token " +  tokens[i] + " for " + 
					s.peek());
		}
	}
}